refactor(llm): remove LangChain imports from core modules#1770
Open
Pouyanpi wants to merge 2 commits intofeat/langchain-decouple/stack-4-rename-asyncfrom
Open
refactor(llm): remove LangChain imports from core modules#1770Pouyanpi wants to merge 2 commits intofeat/langchain-decouple/stack-4-rename-asyncfrom
Pouyanpi wants to merge 2 commits intofeat/langchain-decouple/stack-4-rename-asyncfrom
Conversation
Contributor
Greptile SummaryThis PR removes direct LangChain imports from four core modules as part of the framework-decoupling stack. The changes are: duck-typing
|
| Filename | Overview |
|---|---|
| nemoguardrails/actions/action_dispatcher.py | Replaced isinstance(fn, Runnable) with duck-type hasattr(fn, "ainvoke") and callable(fn.ainvoke) — removes the langchain_core import but broadens the match to any object with a callable ainvoke, which could silently mis-route custom action classes that have ainvoke but don't follow LangChain's input=params calling convention. |
| nemoguardrails/eval/cli.py | LangChain cache imports moved to a lazy try/except block inside check_compliance, making the module importable without LangChain installed. A follow-up fix in this same PR corrects the log order so the success message is only printed after the cache is successfully configured. |
| nemoguardrails/evaluate/evaluate_factcheck.py | Replaced LangChain-based LLM calls with self.llm.generate_async() — removes langchain dependency but introduces an inconsistent async pattern (get_or_create_event_loop().run_until_complete() in create_negative_samples vs asyncio.run() in check_facts) and bypasses the llm_call observability wrapper already present elsewhere in this module (both previously noted). |
| nemoguardrails/library/hallucination/actions.py | Removed the no-op PromptTemplate import; remaining logic is unchanged and uses llm_call / asyncio.gather correctly. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[execute_action called] --> B{action registered?}
B -- No --> Z[return None, failed]
B -- Yes --> C{isclass?}
C -- Yes --> D[instantiate lazy]
C -- No --> E{isfunction or ismethod?}
D --> E
E -- Yes --> F[call fn directly]
F --> G{iscoroutine?}
G -- Yes --> H[await result]
G -- No --> I[log sync warning, use result]
E -- No --> J{"hasattr(ainvoke) and callable? (duck-type for Runnable)"}
J -- Yes --> K["await fn.ainvoke(input=params)"]
J -- No --> L{has run method?}
L -- No --> M[raise Exception]
L -- Yes --> N["call fn.run(**params)"]
H --> O[return result, success]
I --> O
K --> O
N --> O
O --> P{LLMCallException?}
P -- Yes --> Q[re-raise]
P -- No --> R[log warning, return None, failed]
Prompt To Fix All With AI
This is a comment left during a code review.
Path: nemoguardrails/actions/action_dispatcher.py
Line: 219-222
Comment:
**Duck-type check is broader than the original `Runnable` guard**
The old `isinstance(fn, Runnable)` only matched LangChain `Runnable` objects. The new check matches *any* registered action that has a callable `ainvoke` attribute, and then unconditionally calls it as `fn.ainvoke(input=params)` — the LangChain keyword argument convention. A custom action class that exposes `ainvoke` with a different signature (e.g. positional args, or no `input=` key) will never reach the `run()` fallback; it will raise a `TypeError` that is swallowed by the broad `except Exception` handler, returning `"failed"` silently.
If the intent is to keep this path strictly for LangChain `Runnable`-compatible objects, consider tightening the guard or documenting the expected calling convention in the comment:
```suggestion
elif hasattr(fn, "ainvoke") and callable(fn.ainvoke): # type: ignore[union-attr]
# Duck-type check for objects that follow the LangChain Runnable
# protocol (ainvoke(input=...) signature) without importing langchain.
# Custom actions with a differently-signed ainvoke should use 'run' instead.
result = await fn.ainvoke(input=params) # type: ignore[union-attr]
```
How can I resolve this? If you propose a fix, please make it concise.Reviews (6): Last reviewed commit: "fix(eval): log cache status after enabli..." | Re-trigger Greptile
b9bb707 to
41c9073
Compare
3807a9a to
77864fa
Compare
77864fa to
f2a54ce
Compare
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
This was referenced Apr 8, 2026
41c9073 to
00cfc93
Compare
f2a54ce to
51565e8
Compare
00cfc93 to
0991b48
Compare
51565e8 to
514620d
Compare
tgasser-nv
approved these changes
Apr 13, 2026
Collaborator
tgasser-nv
left a comment
There was a problem hiding this comment.
Looks good, just a minor logging nit
- Remove PromptTemplate from hallucination actions (was a no-op) - Replace Runnable isinstance check with duck-type ainvoke check in action_dispatcher - Remove PromptTemplate and llm.bind().invoke() from evaluate_factcheck, use LLMModel.generate_async via event loop - Make eval CLI LangChain cache a lazy optional import
0991b48 to
61abe37
Compare
514620d to
5ed106b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of the LangChain decoupling stack:
Description